#include "Server.h"
#include <iostream>
#include "GMSetID.h"
#include "MsgRecv.h"
#include "Timer.h"
#include "AcceptConnectionThread.h"
#include "GMClientDisconnectRequest.h"
#include "GMServerClientPing.h"
#include "GMClientServerPing.h"
#include "GMSetTime.h"
#include "GameMessageFactory.h"
#include "Networking.h"
#include "IPGetter.h"
#include "Engine.h"
#include "GameState.h"
/************************************************************************/
/* SERVER                                                               */
/************************************************************************/
Server::Server() : 
m_idGenerator(0),
m_timeStamp(0), 
m_gameTickCounter(0), 
m_open(false), 
m_acThread(NULL)
{
}
void Server::ShutDown()
{
    int ret = 0;
    //clean threads
    if(m_acThread)
    {
        m_acThread->StopAccepting();
        ret = m_acThread->WaitForFinish(); //max 1 second
        delete m_acThread;
        m_acThread = NULL;
    }
    //
    for(mMsgRecvs::iterator it = m_msgRecvThreads.begin();
        it != m_msgRecvThreads.end();
        ++it)
    {
        it->second->Halt();
        int t = it->second->WaitForFinish();
        delete it->second;
    }
    m_msgRecvThreads.clear();
    //clean sockets
    for(IDMap::iterator it = m_clients.begin(); 
        it != m_clients.end();
        ++it)
    {
        it->second->Invalidate();
        delete it->second;
    }
    m_clients.clear();

    //clean messages
    m_messageQueue.clean();
    m_open = false;
    OUT_STRING("ACThread returned: " << ret);
}
void Server::SetIDs()
{
    OUT_STRING("Setting the Player ID's...");
    //Itterate over all clients and check they are still valid
    for(IDMap::iterator it = m_clients.begin();
        it != m_clients.end(); ++it)
    {
        GMSetID m;
        m.SetID(it->first);
        m.SetTimeStamp(m_timeStamp);
        SendToClient(m, it->first);
    }
}
void Server::AddClient( Networking::Socket* client )
{
    if(!client)
    {
        OUT_STRING("Invalid client passed to Server::AddClient");
        return;
    }
    MsgRecv* mr = new MsgRecv;
    m_clients[m_idGenerator] = client;
    m_msgRecvThreads[m_idGenerator] = mr;
    m_idGenerator++;
    mr->SetSocket(client);
    mr->Start();
}
void Server::Update()
{
    TheTimer::Instance()->Update();
    m_timeStamp += TheTimer::Instance()->GetDtMilliSeconds();
    ManageClients();
    m_gameTickCounter += TheTimer::Instance()->GetDtMilliSeconds();
    if(m_gameTickCounter > 1000)
    {
        m_gameTickCounter = 0;
        UpdateTimes();
    }
    bool resul = false;
    GameMessage* gm = NULL;
    gm = m_messageQueue.try_pop(&resul);
    while(resul) //Ask for a msg
    {
        if(gm && gm->GetTimeStamp() < m_timeStamp) //Check its valid
        {
            gm->Execute();
            delete gm;
            //and loop
            gm = m_messageQueue.try_pop(&resul);
        }
        else if (gm) //if not, add it back in at the front (faster than push
        {
            m_messageQueue.pushFront(gm);
            break;
        }
    }
    m_messageQueue.UpdateTimeStamps(TheTimer::Instance()->GetDtMilliSeconds());
}
void Server::ManageClients()
{
    //Itterate over all clients and check they are still valid
    for(IDMap::iterator it = m_clients.begin();
        it != m_clients.end(); /*  intentional */)
    {
        if(!it->second->IsOpen())
        {
            OUT_STRING("Socket : " << it->first<< " is not open, removing");
            Engine::Instance()->GetCurrentState()->DestroyPlayer(it->first);
            RemoveClient(it->first);
            it = m_clients.begin();
        }
        else 
            ++it;
    }

	//check all he msg recv functions are going OK
	for(mMsgRecvs::iterator it = m_msgRecvThreads.begin();
		it != m_msgRecvThreads.end(); /*   intentional  */)
	{
		if(it->second->HasStopped())
		{
			//this Msg Recv thread has encountered an issue,
			//we need to stop this client!
			int clientID = it->first;
			RemoveClient(clientID);
			it = m_msgRecvThreads.begin();
		}
		else
			++it;
	}
}
void Server::RemoveClient( int id )
{
    OUT_STRING("Remove Client request! ID: " << id);
    //Stop the MSG RCV thread first,
    mMsgRecvs::iterator jt = m_msgRecvThreads.find(id);
    if(jt != m_msgRecvThreads.end())
    {
        jt->second->Halt();
        int t = jt->second->WaitForFinish();
        delete jt->second;
        m_msgRecvThreads.erase(jt);
    }
    //Now it's safe to delete the socket
    IDMap::iterator it = m_clients.find(id);
    if(it != m_clients.end())
    {
        it->second->Close();
        delete it->second;
        m_clients.erase(it);
    }

    //GAME SPECIFIC CODE
    Engine::Instance()->GetCurrentState()->DestroyPlayer(id);
}
void Server::StartUp( int port )
{
    if(m_acThread == NULL)
    {
        SDLNet_Init();
        GameMessage::StaticPrepareFactory();
        m_acThread = new AcceptConnectionThread;
        m_acThread->SetListenPort(port);
        m_acThread->Start();
        m_open = true;

        IPGetter g;
        g.GetIP();
    }
}
void Server::SetIP(char* ip)
{
    m_ipAddr = ip;
}
void Server::AddMessageToQueue( GameMessage* gm )
{
    m_messageQueue.push(gm);    
}
bool Server::PingClient( int id )
{
    OUT_STRING("Ping Client request! ID: " << id);
    //Send the ping here
    GMServerClientPing gm;
    gm.SetTimeStamp(m_timeStamp);
    return SendToClient(gm, id);
}
void Server::UpdateTimes()
{
    GMSetTime m;
    m.SetTimeStamp(m_timeStamp);
    SendToAllClients(m);
}
void Server::SendToAllClients(GameMessage& gm)
{
    for(IDMap::iterator it = m_clients.begin();
        it != m_clients.end(); ++it)
    {
        if(it->second->IsOpen())
        {
			gm.SetTimeStamp(m_timeStamp);
            if(!SendGM(gm,it->second))
            {
                OUT_STRING("send GM failed...Removing Client: " << it->first);
                it->second->Invalidate();
            }
        }
    }
}
bool Server::SendToClient( GameMessage& gm, int id )
{
    IDMap::iterator it = m_clients.find(id);
    if(it != m_clients.end())
    {
        if(it->second->IsOpen())
        {
			gm.SetTimeStamp(m_timeStamp);
            if(!SendGM(gm,it->second))
            {
                OUT_STRING("Sending message failed...removing Client: " << id);
                it->second->Close();
            }
            return true;
        }
        return false;
    }
    return false;
}

/************************************************************************/
/* MESSAGES                                                             */
/************************************************************************/
/* PrepareGameMessage does what needs to be done to EVERY game message
 As we added the timestamp, we needed to make sure to read the time
 stamp in in both RecvGM AND AddGm, this ensures we don't forget to do it
 in one of the functions. We now need to check the return value is not NULL
 */
GameMessage* PrepareGameMessage(Networking::SockMsg& sm)
{
    std::string type;
    if(!sm.ReadString(&type))
    {
        OUT_STRING("SockMsg->ReadString failed, can not deduce GM type. Returning NULL");
        return NULL;
    }
    int ts = 0;
    if(!sm.ReadInt(&ts))
    {
        OUT_STRING("SockMsg->ReadInt failed, invalid timestamp. Returning NULL");
        return NULL;
    }
    OUT_STRING("Message Received, type: " << type << " , timeStamp:  "
        << ts);
    GameMessage* m  = TheGameMessageFactory::Instance()->Create(type);
    if(!m)
    {
        OUT_STRING("GameMessage Invalid, type: " << type);
        return NULL;
    }
    m->SetTimeStamp(ts);
    return m;
}
//Adds a message to the queue
bool AddGm( Networking::SockMsg& sm )
{
    //Initial error
    if(sm.Size() <= 0)
        return false;
	bool ret = true;
    while((unsigned int)sm.GetIndex() < sm.Size())
    {
        bool currentvalid = true;
        GameMessage* m = PrepareGameMessage(sm);        
        if(m == NULL)
        {
            OUT_STRING("PrepareGameMessage returned NULL in AddGm, returning false...");
            ret = false;
            currentvalid = false;
        }
        if(currentvalid && !m->ReadFromSckMsg(&sm))
        {
            OUT_STRING("GameMessage->ReadFromSckMsg failed, returning false...");
            ret = false;
			delete m;
			m = NULL;
        }
        //At this point add to message Queue
		if(currentvalid && m)
			TheServer::Instance()->AddMessageToQueue(m);

		if(!currentvalid || !ret)
			return false;
    }
    return ret;
}
//posts a msg down the socket
bool SendGM(GameMessage& gm, Networking::Socket* sock)
{
    if(!sock)
    {
        OUT_STRING("Null Socket passes to SendGM, returning false");
        return false;
    }
    Networking::SockMsg sckMsg;
    sckMsg.WriteString(gm.GetTypeName());
    sckMsg.WriteInt(gm.GetTimeStamp());
    gm.WriteToSckMsg(&sckMsg);
    return sock->Send(sckMsg);
}
//Receives a GM and executes it
bool RecvGM(Networking::SockMsg& sm)
{
    if(sm.Size() <= 0)
        return false;
	bool ret = true;
	while((unsigned int)sm.GetIndex() < sm.Size())
	{
		bool currentvalid = true;
		GameMessage* m = PrepareGameMessage(sm);
		if(m == NULL)
		{
			OUT_STRING("PrepareGameMessage returned NULL in RecvGM, returning false...");
			ret = false;
            currentvalid = false;
		}
		if(currentvalid && !m->ReadFromSckMsg(&sm))
		{
			OUT_STRING("GameMessage->ReadFromSckMsg failed, returning false...");
			delete m;
			m = NULL;
            currentvalid = false;
			ret = false;
		}
		if(currentvalid && !m->Execute())
		{
			OUT_STRING("GameMessage->Execute failed, returning false...");
			delete m;
			m = NULL;
			ret = false;
		}
		if(m)
			delete m;
		if(!currentvalid || !ret)
			return false;
	}
    return ret;
}

#include "Engine.h"
#include "GameState.h"
void DBO::DebugOut(std::ostringstream& oss)
{
	oss << '\n';
	std::string str = oss.str();
	Engine::Instance()->GetCurrentState()->HandleMessage(str.c_str());
}
